<?php


namespace App\Service\RiskCheck;

use App\Constants\XZXResponseCode;
use App\Model\Dao\RiskRuleBanRangeDao;
use App\Model\Dao\RiskRuleBlacktextDao;
use App\Model\Dao\RiskRuleParamDao;
use App\Model\Enum\RiskRuleActionEnum;
use App\Model\Enum\RiskRuleBanRangeTypeEnum;
use App\Model\Enum\RiskRuleBlackTypeEnum;
use App\Model\Enum\RiskRuleProcessTypeEnum;
use App\Utils\CustomUtils;
use App\Utils\RiskCheckUtils;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Arr;

/**
 * Class CommonRiskCheckService
 * 通用风控处理逻辑
 *
 * @package App\Service\RiskCheck
 */
class CommonRiskCheckService {

    /**
     * @var RiskRuleBlacktextDao
     * @Inject()
     */
    protected $RiskRuleBlacktextDao;

    /**
     * @var RiskRuleBanRangeDao
     * @Inject()
     */
    protected $RiskRuleBanRangeDao;

    /**
     * @var RiskRuleParamDao
     * @Inject()
     */
    protected $RiskRuleParamDao;

    /**
     * 通用风控规则处理
     *
     * @param array $params
     * @param array $ruleList 规则列表
     *
     * @return array
     */
    public function handle ($params, $ruleList) {

        foreach ($ruleList as $rule) {

            $paramId = $rule['paramId'];

            // 获取参数信息
            $check = $this->RiskRuleParamDao->readParamInfo($paramId);
            CustomUtils::xzxDebug("全局风控规则参数信息", $check);

            if (!$check)
                continue;

            try {
                $this->_check($rule, $check, $params);
            } catch (\Exception $err) {

                $message = $err->getMessage();

                $data = [
                    'ruleId'       => $rule['ruleId'],
                    'paramId'      => $rule['paramId'],
                    'collectionId' => $rule['collectionId'],
                    'paramName'    => $check['paramName'],
                    'message'      => $message,
                ];

                return CustomUtils::buildError($message, XZXResponseCode::FAILED_CODE, $data);
            }
        }

        return CustomUtils::buildSuccess();
    }

    public function clearCache ($paramId) {
        $this->RiskRuleParamDao->clearParamInfo($paramId);
    }

    /**
     * 风控规则参数比较
     *
     * @param $params
     * @param $rule
     * @param $check
     *
     * @throws \Exception
     */
    protected function _check ($rule, $check, $params) {

        $processType      = $rule['processType'];
        $paramName        = $check['paramName'];
        $paramDisplayName = $check['paramDisplayName'];

        if (!Arr::exists($params, $paramName)) {
            throw new \Exception(trans('dispatch.check.common.not_exist', [
                'display_name' => $paramDisplayName,
            ]));
        }

        switch ($processType) {
            // 周期限制
            case RiskRuleProcessTypeEnum::CYCLE_LIMIT:
                $this->_checkProcessCycleLimit($rule, $check, $params);
                break;
            // 黑名单
            case RiskRuleProcessTypeEnum::BLACKLIST:
                $this->_checkProcessBlacklist($rule, $check, $params);
                break;
            // Ban范围
            case RiskRuleProcessTypeEnum::BAN_RANGE_LIST:
                $this->_checkProcessBanRange($rule, $check, $params);
                break;
            default:
                $this->_checkProcessNormal($rule, $check, $params);
                break;
        }

    }

    protected function _checkProcessCycleLimit ($rule, $check, $params) {
        // TODO::周期匹配
    }

    /**
     * Ban范围
     *
     * @param $rule
     * @param $check
     * @param $params
     *
     * @throws \Exception
     */
    protected function _checkProcessBanRange ($rule, $check, $params) {

        $refType          = $rule['refType'];
        $paramName        = $check['paramName'];
        $paramDisplayName = $check['paramDisplayName'];

        $checkValue = $params[$paramName];

        if ($refType == RiskRuleBanRangeTypeEnum::CARD_BIN) {
            $checkValue = substr($checkValue, 0, 6);
        }

        $where = [
            ['startValue', '<=', $checkValue],
            ['endValue', '>=', $checkValue],
            'banType' => $refType,
            'status'  => 1,
        ];

        $count = $this->RiskRuleBanRangeDao->getWhereCount($where);

        if ($count > 0) {
            throw new \Exception(trans('dispatch.check.common.in_ban_range', [
                'display_name' => $paramDisplayName,
            ]));
        }

    }

    /**
     * 黑名单风控
     *
     * @param $rule
     * @param $check
     * @param $params
     *
     * @throws \Exception
     */
    protected function _checkProcessBlacklist ($rule, $check, $params) {

        $refType          = $rule['refType'];
        $paramName        = $check['paramName'];
        $paramDisplayName = $check['paramDisplayName'];

        $checkValue = $params[$paramName];

        $where = [
            'blackType' => $refType,
            'blackText' => $checkValue,
            'status'    => 1,
        ];

        if ($refType == RiskRuleBlackTypeEnum::CARD_NO) {
            // 若为卡号,取前六位匹配
            $where['blackText'] = substr($checkValue, 0, 6);
        }

        // 查询符合条件的数量
        $count = $this->RiskRuleBlacktextDao->getWhereCount($where);

        if ($count > 0) {
            throw new \Exception(trans('dispatch.check.common.hit_blacklist', [
                'display_name' => $paramDisplayName,
            ]));
        }
    }

    /**
     * 常规比较
     *
     * @param $rule
     * @param $check
     * @param $params
     *
     * @return void
     * @throws \Exception
     */
    protected function _checkProcessNormal ($rule, $check, $params) {

        $processType      = $rule['processType'];
        $action           = $rule['action'];
        $paramName        = $check['paramName'];
        $paramDisplayName = $check['paramDisplayName'];

        $checkValue    = $params[$paramName];
        $contrastValue = $check['paramValue'];

        $bool = true;

        switch ($processType) {
            case RiskRuleProcessTypeEnum::NOT_EQUAL:
                $bool = RiskCheckUtils::neqHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::EQUAL:
                $bool = RiskCheckUtils::eqHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::GREATER_THAN:
                $bool = RiskCheckUtils::gtHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::LESS_THAN:
                $bool = RiskCheckUtils::ltHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::INCLUDE:
                $bool = RiskCheckUtils::inHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::EXCLUDE:
                $bool = RiskCheckUtils::notInHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::START_BY:
                $bool = RiskCheckUtils::startByHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::END_BY:
                $bool = RiskCheckUtils::endByHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::NOT_START_BY:
                $bool = RiskCheckUtils::notStartByHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::NOT_END_BY:
                $bool = RiskCheckUtils::notEndByHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::BETWEEN:
                $bool = RiskCheckUtils::inRangeHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::NOT_BETWEEN:
                $bool = RiskCheckUtils::outRangeHandle($checkValue, $contrastValue);
                break;
            case RiskRuleProcessTypeEnum::REGEX:
                $bool = RiskCheckUtils::regexHandle($checkValue, $contrastValue);
                break;
        }

        // var_dump($paramName, $action, $bool);

        // (匹配后拒绝 && 匹配) || (匹配后接受 && 失配)
        if (($action == RiskRuleActionEnum::REJECT && $bool) || ($action == RiskRuleActionEnum::ACCEPT && !$bool)) {
            throw new \Exception(trans('dispatch.check.common.fail', [
                'display_name' => $paramDisplayName,
            ]));
        }
    }
}
